package com.vinxin.app.utils


import cn.hutool.log.Log
import cn.hutool.log.LogFactory
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.core.json.JsonWriteFeature
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer
import com.vinxin.app.exception.ResponseCodeEnum
import com.vinxin.app.exception.VXException
import java.io.IOException
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.*

class JacksonUtils {
    init {
        val javaTimeModule = JavaTimeModule()
        // 序列化配置,针对java8 时间
        javaTimeModule.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
        javaTimeModule.addSerializer(LocalDate::class.java, LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
        javaTimeModule.addSerializer(LocalTime::class.java, LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")))

        // 反序列化配置,针对java8 时间
        javaTimeModule.addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
        javaTimeModule.addDeserializer(LocalDate::class.java, LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
        javaTimeModule.addDeserializer(LocalTime::class.java, LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")))

        mapper.registerModules(javaTimeModule)
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
            .configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), true)
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
    }

    companion object {
        private val log: Log = LogFactory.get()

        /**
         * 获取MAPPER
         */
        val mapper: ObjectMapper = ObjectMapper()

        /**
         * Object to json string
         */
        fun toJson(obj: Any?): String {
            try {
                return mapper.writeValueAsString(obj)
            } catch (e: IOException) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, "toJson error")
            }
        }

        /**
         * Json string deserialize to Jackson [JsonNode].
         *
         * @param json json string
         * @return [JsonNode]
         */
        private fun toObj(json: String?): JsonNode {
            try {
                return mapper.readTree(json)
            } catch (e: IOException) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, "toObj error")
            }
        }

        /**
         * Json string deserialize to Object.
         *
         * */
        fun <T> toObj(json: String?, typeReference: TypeReference<T>?): T {
            try {
                return mapper.readValue(json, typeReference)
            } catch (e: IOException) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, "toObj error")
            }
        }

        /**
         * Json string deserialize to Object.
         *
         * */
        private fun <T> toObj(json: String?, type: Class<T>?): T {
            try {
                return ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).readValue(json, type)
            } catch (e: IOException) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, e)
            }
        }

        /**
         * 类型转换
         */
        fun <T, S> toObj(obj: S, type: Class<T>?): T {
            if (Objects.isNull(obj)) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, "Not allow empty obj.")
            }
            return toObj(toJson(obj), type)
        }

        /**
         * 创建一个json对象
         */
        fun create(): ObjectNode {
            return mapper.createObjectNode()
        }

        /**
         * 转List
         */
        fun <T> toList(json: String?, clazz: Class<T>?): List<T> {
            try {
                val tree = mapper.readTree(json)
                val list: MutableList<T> = ArrayList()
                for (jsonNode in tree) {
                    list.add(mapper.treeToValue(jsonNode, clazz))
                }
                return list
            } catch (e: JsonProcessingException) {
                throw VXException(ResponseCodeEnum.PARSING_JSON_ERROR, "toList error")
            }
        }
    }
}
